package controller.webservice;

import java.util.ArrayList;
import java.util.Collections;
import java.util.NoSuchElementException;

import manager.BidMgr;
import manager.CompletedCourseMgr;
import manager.CourseMgr;
import manager.CoursePreRequisiteMgr;
import manager.SectionMgr;
import manager.SectionStudentMgr;
import manager.StudentMgr;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import util.DateUtil;
import util.JsonUtil;
import controller.RoundCtrl;
import controller.comparator.BidComparatorByBidAmountAndUserId;
import controller.comparator.BidComparatorByCourseCodeAndSectionCodeAndHighestBidAmountAndUserId;
import controller.comparator.BidComparatorByCourseCodeAndUserId;
import controller.comparator.BidComparatorByUserId;
import controller.comparator.CourseComparatorByCourseCode;
import controller.comparator.SectionComparatorByCourseCodeAndSectionCode;
import controller.comparator.StudentComparatorByUserId;
import controller.validator.CommonValidator;
import controller.validator.Validator;
import entity.Bid;
import entity.Course;
import entity.Round;
import entity.Section;
import entity.Student;
import exception.InputValidationErrors;

/**
 * Exposes methods to dump database records.
 * 
 * @author mohameds.2012, sheryl.tay.2012, jaslin.wee.2012, huangjm.2012,
 *         huiwensong.2012
 * 
 */
public class DumpCtrl {

	private static final String TABLE_COURSE = "course";
	private static final String TABLE_SECTION = "section";
	private static final String TABLE_STUDENT = "student";
	private static final String TABLE_PREREQUISITE = "prerequisite";
	private static final String TABLE_BID = "bid";
	private static final String TABLE_COMPLETED_COURSE = "completed-course";
	private static final String TABLE_SECTION_STUDENT = "section-student";

	private static final String FIELD_COURSE = "course";
	private static final String FIELD_CODE = "code";
	private static final String FIELD_SCHOOL = "school";
	private static final String FIELD_TITLE = "title";
	private static final String FIELD_DESCRIPTION = "description";
	private static final String FIELD_EXAM_DATE = "exam date";
	private static final String FIELD_EXAM_START = "exam start";
	private static final String FIELD_EXAM_END = "exam end";
	private static final String FIELD_USERID = "userid";
	private static final String FIELD_PASSWORD = "password";
	private static final String FIELD_NAME = "name";
	private static final String FIELD_EDOLLAR = "edollar";
	private static final String FIELD_START = "start";
	private static final String FIELD_END = "end";
	private static final String FIELD_INSTRUCTOR = "instructor";
	private static final String FIELD_AMOUNT = "amount";
	private static final String FIELD_SIZE = "size";
	private static final String FIELD_VENUE = "venue";
	private static final String FIELD_PREREQUISITE = "prerequisite";
	private static final String FIELD_SECTION = "section";
	private static final String FIELD_DAY = "day";

	private StudentMgr studentMgr;
	private CourseMgr courseMgr;
	private CoursePreRequisiteMgr coursePrerequisiteMgr;
	private CompletedCourseMgr completedCourseMgr;
	private SectionMgr sectionMgr;
	private BidMgr bidMgr;
	private SectionStudentMgr sectionStudentMgr;
	private RoundCtrl roundCtrl;
	private CommonValidator commonValidator = new CommonValidator();

	public DumpCtrl() {
		studentMgr = new StudentMgr();
		courseMgr = new CourseMgr();
		coursePrerequisiteMgr = new CoursePreRequisiteMgr();
		completedCourseMgr = new CompletedCourseMgr();
		sectionMgr = new SectionMgr();
		bidMgr = new BidMgr();
		sectionStudentMgr = new SectionStudentMgr();
		roundCtrl = new RoundCtrl();
	}

	/**
	 * Dumps all records in the database.
	 * 
	 * @return a JSON Object
	 */
	public JSONObject dumpTable() {
		JSONObject dumpTableJsonObj = new JSONObject();

		dumpTableJsonObj.put("status", "success");
		dumpTableJsonObj.put(TABLE_COURSE, dumpCourseTable());
		dumpTableJsonObj.put(TABLE_SECTION, dumpSectionTable());
		dumpTableJsonObj.put(TABLE_STUDENT, dumpStudentTable());
		dumpTableJsonObj.put(TABLE_PREREQUISITE, dumpPrerequisiteTable());
		dumpTableJsonObj.put(TABLE_BID, dumpBidTable());
		dumpTableJsonObj
				.put(TABLE_COMPLETED_COURSE, dumpCompletedCourseTable());
		dumpTableJsonObj.put(TABLE_SECTION_STUDENT, dumpSectionStudentTable());

		return dumpTableJsonObj;
	}

	private JSONArray dumpCourseTable() {

		JSONArray dumpCourseJsonArray = new JSONArray();

		ArrayList<Course> courses = courseMgr.retrieveAllCourses();
		Collections.sort(courses, new CourseComparatorByCourseCode());

		for (Course eachCourse : courses) {

			JSONObject dumpCourseJsonObj = new JSONObject();

			dumpCourseJsonObj.put(FIELD_COURSE, eachCourse.getCourseCode());
			dumpCourseJsonObj.put(FIELD_SCHOOL, eachCourse.getSchool());
			dumpCourseJsonObj.put(FIELD_TITLE, eachCourse.getTitle());
			dumpCourseJsonObj.put(FIELD_DESCRIPTION,
					eachCourse.getDescription());
			dumpCourseJsonObj.put(FIELD_EXAM_DATE,
					eachCourse.getExamDate(DateUtil.OUTPUT_DATE_PATTERN));
			dumpCourseJsonObj.put(FIELD_EXAM_START,
					eachCourse.getExamStartTime(DateUtil.OUTPUT_TIME_PATTERN));
			dumpCourseJsonObj.put(FIELD_EXAM_END,
					eachCourse.getExamEndTime(DateUtil.OUTPUT_TIME_PATTERN));

			dumpCourseJsonArray.add(dumpCourseJsonObj);

		}

		return dumpCourseJsonArray;
	}

	private JSONArray dumpStudentTable() {

		JSONArray dumpStudentJsonArray = new JSONArray();

		ArrayList<Student> students = studentMgr.retrieveAllStudents();
		Collections.sort(students, new StudentComparatorByUserId());

		for (Student eachStudent : students) {

			JSONObject studentJsonObj = new JSONObject();

			studentJsonObj.put(FIELD_USERID, eachStudent.getUserId());
			studentJsonObj.put(FIELD_PASSWORD, eachStudent.getPassword());
			studentJsonObj.put(FIELD_NAME, eachStudent.getName());
			studentJsonObj.put(FIELD_SCHOOL, eachStudent.getSchool());
			studentJsonObj.put(FIELD_EDOLLAR, eachStudent.getEdollar());

			dumpStudentJsonArray.add(studentJsonObj);

		}

		return dumpStudentJsonArray;
	}

	private JSONArray dumpSectionTable() {

		JSONArray dumpSectionJsonArray = new JSONArray();

		ArrayList<Section> sections = sectionMgr.retrieveAllSections();
		Collections.sort(sections,
				new SectionComparatorByCourseCodeAndSectionCode());

		for (Section eachSection : sections) {
			JSONObject dumpSectionJsonObj = new JSONObject();

			dumpSectionJsonObj.put(FIELD_COURSE, eachSection.getCourse()
					.getCourseCode());
			dumpSectionJsonObj.put(FIELD_SECTION, eachSection.getSectionCode());
			dumpSectionJsonObj.put(FIELD_DAY,
					DateUtil.dayOfWeekLong(eachSection.getDayOfWeek()));
			dumpSectionJsonObj.put(FIELD_START,
					eachSection.getStartTime(DateUtil.OUTPUT_TIME_PATTERN));
			dumpSectionJsonObj.put(FIELD_END,
					eachSection.getEndTime(DateUtil.OUTPUT_TIME_PATTERN));
			dumpSectionJsonObj.put(FIELD_INSTRUCTOR,
					eachSection.getInstructor());
			dumpSectionJsonObj.put(FIELD_VENUE, eachSection.getVenue());
			dumpSectionJsonObj.put(FIELD_SIZE, eachSection.getSize());

			dumpSectionJsonArray.add(dumpSectionJsonObj);
		}

		return dumpSectionJsonArray;
	}

	private JSONArray dumpBidTable() {

		JSONArray bidJsonArray = new JSONArray();

		ArrayList<Bid> bids = null;

		if (roundCtrl.isCurrentRoundNo(Round.ROUND_TWO)) {
			bids = bidMgr.retrieveLastBidInEachSection();
		} else {
			bids = bidMgr.retrieveAllBids();
		}

		Collections
				.sort(bids,
						new BidComparatorByCourseCodeAndSectionCodeAndHighestBidAmountAndUserId());
		for (Bid eachBid : bids) {

			JSONObject bidJsonObj = new JSONObject();

			bidJsonObj.put(FIELD_USERID, eachBid.getStudent().getUserId());
			bidJsonObj.put(FIELD_AMOUNT, eachBid.getBidAmount());
			bidJsonObj.put(FIELD_CODE, eachBid.getSection().getCourse()
					.getCourseCode());
			bidJsonObj
					.put(FIELD_SECTION, eachBid.getSection().getSectionCode());
			bidJsonArray.add(bidJsonObj);
		}

		return bidJsonArray;
	}

	private JSONArray dumpSectionStudentTable() {

		JSONArray sectionStudentJsonArray = new JSONArray();

		ArrayList<Bid> successfulBids = sectionStudentMgr
				.retrieveAllSuccessfulBids();
		Collections.sort(successfulBids,
				new BidComparatorByCourseCodeAndUserId());

		for (Bid eachBid : successfulBids) {
			JSONObject bidJsonObj = new JSONObject();

			bidJsonObj.put(FIELD_USERID, eachBid.getStudent().getUserId());
			bidJsonObj.put(FIELD_COURSE, eachBid.getSection().getCourse()
					.getCourseCode());
			bidJsonObj
					.put(FIELD_SECTION, eachBid.getSection().getSectionCode());
			bidJsonObj.put(FIELD_AMOUNT, eachBid.getBidAmount());

			sectionStudentJsonArray.add(bidJsonObj);
		}

		return sectionStudentJsonArray;
	}

	private JSONArray dumpPrerequisiteTable() {

		JSONArray prerequisiteJsonArray = new JSONArray();

		ArrayList<Course> courses = coursePrerequisiteMgr
				.retrieveAllCoursesThatHavePrerequisite();
		Collections.sort(courses, new CourseComparatorByCourseCode());

		for (Course eachCourse : courses) {
			JSONObject prerequisiteJsonObj = new JSONObject();

			ArrayList<Course> prerequisites = coursePrerequisiteMgr
					.retrievePreRequisitesForCourse(eachCourse.getCourseCode());
			Collections.sort(prerequisites, new CourseComparatorByCourseCode());

			for (Course eachPrerequisite : prerequisites) {

				prerequisiteJsonObj.put(FIELD_COURSE,
						eachCourse.getCourseCode());
				prerequisiteJsonObj.put(FIELD_PREREQUISITE,
						eachPrerequisite.getCourseCode());

				prerequisiteJsonArray.add(prerequisiteJsonObj);

			}
		}

		return prerequisiteJsonArray;
	}

	private JSONArray dumpCompletedCourseTable() {

		JSONArray completedCourseJsonArray = new JSONArray();

		ArrayList<Course> courses = completedCourseMgr
				.retrieveAllCompletedCourses();
		Collections.sort(courses, new CourseComparatorByCourseCode());

		for (Course eachCourse : courses) {
			JSONObject completedCourseJsonObj = new JSONObject();

			ArrayList<Student> students = completedCourseMgr
					.retrieveStudentsWhoCompletedCourse(eachCourse
							.getCourseCode());
			Collections.sort(students, new StudentComparatorByUserId());

			for (Student eachStudent : students) {

				completedCourseJsonObj.put(FIELD_USERID,
						eachStudent.getUserId());
				completedCourseJsonObj.put(FIELD_CODE,
						eachCourse.getCourseCode());

				completedCourseJsonArray.add(completedCourseJsonObj);

			}
		}

		return completedCourseJsonArray;
	}

	/**
	 * Retrieves the bidding information of a specific section for the current
	 * bidding round. If no bidding rounds are active, the information for the
	 * most recently concluded round is dumped.
	 * 
	 * @param courseCode
	 *            course code of the course to which the section belongs to
	 * @param sectionCode
	 *            section code of the section
	 * @return JSON object containing the bids data
	 * 
	 */
	public JSONObject dumpBid(String courseCode, String sectionCode) {

		/*
		 * Validation for invalid course & invalid section
		 */
		try {
			commonValidator.checkForCourseAndSection(courseCode, sectionCode);
		} catch (Exception e) {
			InputValidationErrors ive = new InputValidationErrors(
					e.getMessage());
			return JsonUtil.newJsonErrorResponse(ive.toJsonArray());
		}

		JSONObject bidsJsonObj = new JSONObject();
		JSONArray bidsJsonArray = new JSONArray();

		ArrayList<Bid> bids = bidMgr.retrieveCurrentRoundBidResultsForSection(
				courseCode, sectionCode);

		Collections.sort(bids, new BidComparatorByBidAmountAndUserId());

		for (int i = 0; i < bids.size(); i++) {
			Bid eachBid = bids.get(i);

			JSONObject bidJsonObj = new JSONObject();
			bidJsonObj.put("row", i + 1);
			bidJsonObj.put(FIELD_USERID, eachBid.getStudent().getUserId());
			bidJsonObj.put(FIELD_AMOUNT, eachBid.getBidAmount());
			bidJsonObj.put("result", eachBid.getBidResult());

			bidsJsonArray.add(bidJsonObj);
		}

		bidsJsonObj.put("status", "success");
		bidsJsonObj.put("bids", bidsJsonArray);

		return bidsJsonObj;
	}

	/**
	 * Retrieves the information of a specific user.
	 * 
	 * @param userId
	 * @return JSON object containing the user data
	 * 
	 */
	public JSONObject dumpUser(String userId) {
		JSONObject studentJsonObj = new JSONObject();

		try {
			Student user = studentMgr.retrieveStudentForUserId(userId);

			studentJsonObj.put("status", "success");
			studentJsonObj.put(FIELD_USERID, user.getUserId());
			studentJsonObj.put(FIELD_PASSWORD, user.getPassword());
			studentJsonObj.put(FIELD_NAME, user.getName());
			studentJsonObj.put(FIELD_SCHOOL, user.getSchool());
			studentJsonObj.put(FIELD_EDOLLAR, user.getEdollar());

		} catch (NoSuchElementException e) {
			InputValidationErrors ive = new InputValidationErrors(
					Validator.INVALID_USERID);
			return JsonUtil.newJsonErrorResponse(ive.toJsonArray());
		}

		return studentJsonObj;
	}

	/**
	 * Retrieves the information for a section, and it's enrolled students.
	 * During round 2, this will return the enrolled students bid successfully
	 * in round 1. After round 2 is closed, this will return the enrolled
	 * students who bid successfully in round 1 & 2.
	 * 
	 * @param courseCode
	 *            course code of the course to which the section belongs to
	 * @param sectionCode
	 *            section code of the section
	 * @return JSON object containing students
	 * 
	 */
	public JSONObject dumpSection(String courseCode, String sectionCode) {

		/*
		 * Validation for invalid course & invalid section
		 */
		try {
			commonValidator.checkForCourseAndSection(courseCode, sectionCode);
		} catch (Exception e) {
			InputValidationErrors ive = new InputValidationErrors(
					e.getMessage());
			return JsonUtil.newJsonErrorResponse(ive.toJsonArray());
		}

		JSONArray studentsJsonArray = new JSONArray();

		ArrayList<Bid> successfulBids = null;
		if (roundCtrl.isCurrentRoundNo(Round.ROUND_TWO)
				&& !roundCtrl.isCurrentRoundOpen()) {
			successfulBids = sectionStudentMgr
					.retrieveSuccessfulBidsForSection(courseCode, sectionCode);
		} else {
			successfulBids = sectionStudentMgr
					.retrieveSuccessfulBidsForSectionAndBidRoundNo(courseCode,
							sectionCode, Round.ROUND_ONE);
		}

		Collections.sort(successfulBids, new BidComparatorByUserId());

		JSONObject studentsJsonObj = new JSONObject();

		JSONObject studentJsonObj;

		for (Bid eachBid : successfulBids) {

			studentJsonObj = new JSONObject();
			studentJsonObj.put(FIELD_USERID, eachBid.getStudent().getUserId());
			studentJsonObj.put(FIELD_AMOUNT, eachBid.getBidAmount());

			studentsJsonArray.add(studentJsonObj);
		}

		studentsJsonObj.put("status", "success");
		studentsJsonObj.put("students", studentsJsonArray);

		return studentsJsonObj;
	}

}
